package com.codebo.homiematchbackend.controller;

import cn.hutool.core.util.RandomUtil;
import cn.hutool.json.JSONArray;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.codebo.homiematchbackend.annotation.AuthCheck;
import com.codebo.homiematchbackend.common.BaseResponse;
import com.codebo.homiematchbackend.common.DeleteRequest;
import com.codebo.homiematchbackend.common.ErrorCode;
import com.codebo.homiematchbackend.common.ResultUtils;
import com.codebo.homiematchbackend.constant.UserConstant;
import com.codebo.homiematchbackend.exception.BusinessException;
import com.codebo.homiematchbackend.exception.ThrowUtils;
import com.codebo.homiematchbackend.model.dto.user.FollowUserRequest;
import com.codebo.homiematchbackend.model.dto.user.LoginRequest;
import com.codebo.homiematchbackend.model.dto.user.RegisterRequest;
import com.codebo.homiematchbackend.model.dto.user.UserQueryRequest;
import com.codebo.homiematchbackend.model.entity.User;
import com.codebo.homiematchbackend.model.vo.UserDetailVO;
import com.codebo.homiematchbackend.model.vo.UserVO;
import com.codebo.homiematchbackend.service.FollowService;
import com.codebo.homiematchbackend.service.UserService;
import com.github.benmanes.caffeine.cache.Cache;
import com.github.benmanes.caffeine.cache.Caffeine;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.util.DigestUtils;
import org.springframework.web.bind.annotation.*;

import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletRequest;
import java.time.Duration;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;

@RequestMapping("/user")
@RestController
public class UserController
{
    @Resource
    private UserService userService;

    @Resource
    private FollowService followService;

    @Resource
    private StringRedisTemplate stringRedisTemplate;

    private final Cache<String, String> LOCAL_CACHE = Caffeine.newBuilder()
            .initialCapacity(1024)
            .maximumSize(10_000L) // 最大 10000 条
            // 缓存 5 分钟后移除
            .expireAfterWrite(Duration.ofMinutes(5))
            .build();

    @PostMapping("/login")
    public BaseResponse<UserVO> login(@RequestBody LoginRequest loginRequest, HttpServletRequest httpServletRequest)
    {
        ThrowUtils.throwIf(loginRequest == null, ErrorCode.PARAMS_ERROR, "登录参数为空");
        String userAccount = loginRequest.getUserAccount();
        String userPassword = loginRequest.getUserPassword();
        UserVO userVO = userService.login(userAccount, userPassword, httpServletRequest);
        return ResultUtils.success(userVO);
    }

    @PostMapping("/register")
    public BaseResponse<Long> register(@RequestBody RegisterRequest registerRequest)
    {
        System.out.println("registerRequest = " + registerRequest);
        Long id = userService.register(registerRequest);
        return ResultUtils.success(id);
    }

    @GetMapping("/{id}")
    public BaseResponse<UserDetailVO> getUserById(@PathVariable("id") Long id, HttpServletRequest httpServletRequest)
    {
        ThrowUtils.throwIf(id == null || id <= 0, ErrorCode.PARAMS_ERROR);
        User user = userService.getById(id);
        ThrowUtils.throwIf(user == null, ErrorCode.NOT_FOUND_ERROR, "请求用户不存在");
        UserVO userVO = userService.getUserVO(user);

        UserDetailVO userDetailVO = new UserDetailVO();
        userDetailVO.setUserVO(userVO);
        userDetailVO.setIsFollow(followService.isFollow(id, httpServletRequest));

        return ResultUtils.success(userDetailVO);
    }

    @PostMapping("/delete")
    @AuthCheck(mustRole = UserConstant.ADMIN_ROLE)
    public BaseResponse<Boolean> deleteUser(@RequestBody DeleteRequest deleteRequest)
    {
        if (deleteRequest == null || deleteRequest.getId() <= 0)
        {
            throw new BusinessException(ErrorCode.PARAMS_ERROR);
        }
        boolean b = userService.removeById(deleteRequest.getId());
        return ResultUtils.success(b);
    }

    @PostMapping("/follow")
    @AuthCheck(mustRole = UserConstant.DEFAULT_ROLE)
    public BaseResponse<Boolean> followUser(@RequestBody FollowUserRequest followUserRequest)
    {
        ThrowUtils.throwIf(followUserRequest == null, ErrorCode.PARAMS_ERROR);
        boolean result = userService.followUser(followUserRequest);
        return ResultUtils.success(result);
    }

    @PostMapping("/logout")
    public BaseResponse<Boolean> logout(HttpServletRequest httpServletRequest)
    {
        boolean result = userService.logout(httpServletRequest);
        return ResultUtils.success(result);
    }


    @GetMapping("/get/login")
    public BaseResponse<UserVO> getLoginUser(HttpServletRequest httpServletRequest)
    {
        UserVO userVO = userService.getUserVO(userService.getLoginUser(httpServletRequest));
        return ResultUtils.success(userVO);
    }

    @PostMapping("/list/page/vo")
    @AuthCheck(mustRole = UserConstant.ADMIN_ROLE)
    public BaseResponse<Page<UserVO>> listUserVoByPage(@RequestBody UserQueryRequest userQueryRequest)
    {
        ThrowUtils.throwIf(userQueryRequest == null, ErrorCode.PARAMS_ERROR);
        int current = userQueryRequest.getCurrent();
        int pageSize = userQueryRequest.getPageSize();
        //根据条件查询用户
        Page<User> userPage = userService.page(new Page<>(current, pageSize), userService.getQueryWrapper(userQueryRequest));
        //转为vo
        Page<UserVO> userVOPage = new Page<>(current, pageSize, userPage.getTotal());
        userVOPage.setRecords(userService.getUserVOList(userPage.getRecords()));
        return ResultUtils.success(userVOPage);
    }

    @PostMapping("/list/recommend")
    @AuthCheck(mustRole = UserConstant.DEFAULT_ROLE)
    public BaseResponse<Page<UserVO>> listUserByRecommend(@RequestBody UserQueryRequest userQueryRequest, HttpServletRequest httpServletRequest)
    {
        ThrowUtils.throwIf(userQueryRequest == null, ErrorCode.PARAMS_ERROR);
        Boolean isRefresh = userQueryRequest.getIsRefresh();
        ThrowUtils.throwIf(isRefresh == null, ErrorCode.PARAMS_ERROR);
        //构建缓存key
        String queryCondition = JSONUtil.toJsonStr(userQueryRequest)+ JSONUtil.toJsonStr(userService.getLoginUser(httpServletRequest));
        String hashKey = DigestUtils.md5DigestAsHex(queryCondition.getBytes());
        String cacheKey = String.format("HomieMatch:listUserByRecommend:%s", hashKey);

        int current = userQueryRequest.getCurrent();
        int pageSize = userQueryRequest.getPageSize();
        ValueOperations<String, String> opsForValue = stringRedisTemplate.opsForValue();

        if (!isRefresh)
        {
            // 1. 先从本地缓存中查询
            String cachedValue = LOCAL_CACHE.getIfPresent(cacheKey);
            if (cachedValue != null)
            {
                // 如果缓存命中，返回结果
                List<UserVO> cachedList = JSONUtil.toList(new JSONArray(cachedValue), UserVO.class);
                Page<UserVO> cachedPage = new Page<>(current, pageSize, cachedList.size());
                cachedPage.setRecords(cachedList);
                return ResultUtils.success(cachedPage);
            }
            // 2. 本地缓存未命中，查询 Redis 分布式缓存

            cachedValue = opsForValue.get(cacheKey);
            if (cachedValue != null)
            {
                // 如果缓存命中，更新本地缓存，返回结果
                LOCAL_CACHE.put(cacheKey, cachedValue);
                List<UserVO> cachedList = JSONUtil.toList(new JSONArray(cachedValue), UserVO.class);
                Page<UserVO> cachedPage = new Page<>(current, pageSize, cachedList.size());
                cachedPage.setRecords(cachedList);
                return ResultUtils.success(cachedPage);
            }
        }


        // 3. 查询数据库
        //根据条件查询用户
//        Page<User> userPage = userService.page(new Page<>(current, pageSize), userService.getQueryWrapper(userQueryRequest));
        //按相似度进行排序
        List<UserVO> userVOList = userService.listUserByRecommend(httpServletRequest, userQueryRequest);
        if (userVOList.isEmpty())
        {
            return ResultUtils.success(new Page<>(current, pageSize));
        }

        // 4. 更新缓存
        // 更新 Redis 缓存
        String cacheValue = JSONUtil.toJsonStr(userVOList);
        // 设置缓存的过期时间，5 - 10 分钟过期，防止缓存雪崩
        int cacheExpireTime = 300 + RandomUtil.randomInt(0, 300);
        opsForValue.set(cacheKey, cacheValue, cacheExpireTime, TimeUnit.SECONDS);
        // 写入本地缓存
        LOCAL_CACHE.put(cacheKey, cacheValue);

        Page<UserVO> userVOPage = new Page<>(current, pageSize, userVOList.size());
        userVOPage.setRecords(userVOList);
        return ResultUtils.success(userVOPage);
    }


    @GetMapping("/teach_learn")
    public BaseResponse<Map<String, List<String>>> getTeachAndLearn()
    {
        Map<String, List<String>> teachAndLearn = new HashMap<>();

        /* ================= 理工 ================= */
        teachAndLearn.put("计算机", Arrays.asList(
                "Java", "Python", "C++", "C", "C#", "Go", "Rust", "前端", "后端", "算法"));
        teachAndLearn.put("数据科学", Arrays.asList(
                "数据分析", "机器学习", "深度学习", "数据可视化", "SQL", "Excel", "R", "Tableau"));
        teachAndLearn.put("工科基础", Arrays.asList(
                "电路分析", "模拟电子", "数字电子", "自动控制", "信号与系统", "机械制图", "PLC", "嵌入式"));
        teachAndLearn.put("通信网络", Arrays.asList(
                "5G 原理", "物联网", "无线通信", "光纤通信", "网络协议", "网络安全", "Router 配置"));
        teachAndLearn.put("航空航天", Arrays.asList(
                "飞行力学", "轨道力学", "航空电子", "火箭发动机", "卫星通信", "无人机技术"));

        /* ================= 商科 ================= */
        teachAndLearn.put("金融经济", Arrays.asList(
                "微观经济学", "宏观经济学", "货币银行学", "证券投资", "期货期权", "区块链经济", "行为金融"));
        teachAndLearn.put("会计财管", Arrays.asList(
                "会计基础", "财务分析", "成本会计", "管理会计", "审计", "税法", "Excel 财务建模"));
        teachAndLearn.put("市场营销", Arrays.asList(
                "市场调查", "品牌管理", "数字营销", "社交媒体运营", "内容营销", "SEO/SEM", "增长黑客"));

        /* ================= 语言 ================= */
        teachAndLearn.put("英语", Arrays.asList(
                "四级", "六级", "雅思", "托福", "GRE", "口语", "商务英语", "翻译"));
        teachAndLearn.put("东亚语言", Arrays.asList(
                "日语", "韩语", "粤语", "台语", "汉字速记"));
        teachAndLearn.put("欧洲语言", Arrays.asList(
                "法语", "德语", "西班牙语", "俄语", "意大利语", "葡萄牙语"));
        teachAndLearn.put("小语种", Arrays.asList(
                "阿拉伯语", "泰语", "越南语", "印地语", "土耳其语", "斯瓦希里语"));

        /* ================= 数学 & 基础科学 ================= */
        teachAndLearn.put("数学", Arrays.asList(
                "高等数学", "线性代数", "离散数学", "概率论", "数值分析", "运筹学", "数学建模"));
        teachAndLearn.put("物理化学", Arrays.asList(
                "大学物理", "理论力学", "量子力学", "热力学", "电磁学", "无机化学", "有机化学", "分析化学"));

        /* ================= 艺术 & 设计 ================= */
        teachAndLearn.put("艺术", Arrays.asList(
                "音乐理论", "钢琴", "吉他", "声乐", "美术基础", "速写", "油画", "水彩"));
        teachAndLearn.put("设计创意", Arrays.asList(
                "平面设计", "UI/UX", "插画", "动效设计", "品牌视觉", "Figma", "Photoshop", "Blender 建模"));

        /* ================= 社科 ================= */
        teachAndLearn.put("人文社科", Arrays.asList(
                "心理学", "哲学", "历史", "社会学", "新闻传播", "法律基础", "国际关系", "公共演讲"));
        teachAndLearn.put("教育师范", Arrays.asList(
                "教育学原理", "教学设计", "班级管理", "教师资格证", "STEAM 教育", "特殊教育学"));

        /* ================= 生活 & 兴趣 ================= */
        teachAndLearn.put("生活技能", Arrays.asList(
                "烹饪", "烘焙", "咖啡拉花", "调酒", "整理收纳", "理财入门", "副业指南", "时间管理"));
        teachAndLearn.put("运动健康", Arrays.asList(
                "健身", "瑜伽", "游泳", "跑步", "篮球", "羽毛球", "康复训练", "营养学"));

        /* ================= 考试 & 竞赛 ================= */
        teachAndLearn.put("考试竞赛", Arrays.asList(
                "考研数学", "考研英语", "高考物理", "公务员行测", "公务员申论", "教师资格证", "ACM 算法", "数学建模美赛"));

        /* ================= 新兴技术 ================= */
        teachAndLearn.put("新兴技术", Arrays.asList(
                "区块链开发", "智能合约", "NFT", "元宇宙", "Web3.0", "AIGC", "提示工程", "低代码平台"));

        return ResultUtils.success(teachAndLearn);
    }
}
